library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use WORK.constants.all;

entity STATE_MACHINE is
	port (
		-- Systesm signals
		clk					: in std_logic;
		reset				: in std_logic;
		
		-- Input Signals
		launch_scan			:	in std_logic;
		scan_period_elapsed	:	in std_logic;
		end_gw_config		:	in std_logic;
		res_reg_buf_full	:	in std_logic;
		end_of_chain		:	in std_logic;
		irq_ack				:	in std_logic;
		
		-- Output signals
		shift				:	out std_logic;
		capture				:	out std_logic;
		update				:	out std_logic;
		gwen				:	out std_logic;
		resetn				:	out std_logic;
		req_irq				:	out std_logic;
		status				:	out std_logic_vector(STATUS_SIZE-1 downto 0)
	);
end STATE_MACHINE;

architecture BEHAVIORAL of STATE_MACHINE is 
	type state_t is (IDLE, GW_CONFIG, GW_SHIFT, GW_UPDATE, MON_LOAD_VALUE, MON_CAPTURE, MON_SHIFT, MON_UPDATE, LOAD_CONFIG_STORE_RESULT, GW_PASSIVE, IRQ, WAIT_ACK);
	signal current_state	: state_t := IDLE;
	signal next_state		: state_t := IDLE;
	signal A, C, D, E, F, G	: std_logic;
begin
	A	<= launch_scan and scan_period_elapsed;
	C	<= end_gw_config;
	D	<= res_reg_buf_full and end_of_chain;
	E	<= not res_reg_buf_full and end_of_chain;
	F	<= not end_of_chain;
	G	<= irq_ack;
	
	-- purpose : state sequencer
	-- type    : sequential
	P_SEQUENCER : process(clk, reset)
	begin
		if reset = '1' then 
			current_state	<= IDLE;
		elsif clk'event and clk = '1' then
			current_state <= next_state;
		end if;
	end process;
	
	-- purpose : output signal generation
	-- type    : sequential
	P_OUTPUT : process(current_state)
	begin
		case current_state is
			when WAIT_ACK =>
				shift	<=	'0';
				capture	<=	'0';
				update	<=	'0';
				gwen	<=	'0';
				resetn	<=	'0';
				req_irq <=	'0';
				status	<=	"1111";
			
			when IDLE =>
				shift	<=	'0';
				capture	<=	'0';
				update	<=	'0';
				gwen	<=	'0';
				resetn	<=	'0';
				req_irq <=	'0';
				status	<=	STATUS_IDLE;

			when GW_CONFIG =>
				shift	<=	'0';
				capture	<=	'0';
				update	<=	'0';
				gwen	<=	'1';
				resetn	<=	'1';
				req_irq <=	'0';
				status	<=	STATUS_GW_CONFIG;

			when GW_SHIFT =>
				shift	<=	'1';
				capture	<=	'0';
				update	<=	'0';
				gwen	<=	'1';
				resetn	<=	'1';
				req_irq <=	'0';
				status	<=	STATUS_GW_SHIFT;

			when GW_UPDATE =>
				shift	<=	'0';
				capture	<=	'0';
				update	<=	'1';
				gwen	<=	'1';
				resetn	<=	'1';
				req_irq <=	'0';
				status	<=	STATUS_GW_UPDATE;

			when MON_LOAD_VALUE =>
				shift	<=	'0';
				capture	<=	'0';
				update	<=	'0';
				gwen	<=	'1';
				resetn	<=	'1';
				req_irq <=	'0';
				status	<=	STATUS_MON_LOAD_VALUE;

			when MON_CAPTURE =>
				shift	<=	'0';
				capture	<=	'1';
				update	<=	'0';
				gwen	<=	'1';
				resetn	<=	'1';
				req_irq <=	'0';
				status	<=	STATUS_MON_CAPTURE;

			when MON_SHIFT =>
				shift	<=	'1';
				capture	<=	'0';
				update	<=	'0';
				gwen	<=	'1';
				resetn	<=	'1';
				req_irq <=	'0';
				status	<=	STATUS_MON_SHIFT;

			when MON_UPDATE =>
				shift	<=	'0';
				capture	<=	'0';
				update	<=	'1';
				gwen	<=	'0';
				resetn	<=	'1';
				req_irq <=	'0';
				status	<=	STATUS_MON_UPDATE;

			when LOAD_CONFIG_STORE_RESULT =>
				shift	<=	'0';
				capture	<=	'0';
				update	<=	'0';
				gwen	<=	'0';
				resetn	<=	'1';
				req_irq <=	'0';
				status	<=	STATUS_LOAD_CONFIG_STORE_RESULT;

			when GW_PASSIVE =>
				shift	<=	'0';
				capture	<=	'0';
				update	<=	'0';
				gwen	<=	'0';
				resetn	<=	'0';
				req_irq <=	'0';
				status	<=	STATUS_GW_PASSIVE;

			when IRQ =>
				shift	<=	'0';
				capture	<=	'0';
				update	<=	'0';
				gwen	<=	'0';
				resetn	<=	'1';
				req_irq <=	'1';
				status	<=	STATUS_IRQ;

		end case;
	end process P_OUTPUT;
	
	-- purpose : next state selector
	-- type    : sequential
	P_NEXT_STATE : process(current_state, A, C, D, E, F, G)
	begin
		case current_state is
			when WAIT_ACK =>
				if G = '0' then
					next_state <= IDLE;
				else
					next_state <= WAIT_ACK;
				end if;
				
			when IDLE =>
				if A = '1' then
					next_state	<= IDLE;
				else
					next_state	<= GW_CONFIG;
				end if;
				
			when GW_CONFIG =>
				next_state	<= GW_SHIFT;
				
			when GW_SHIFT =>
				if C = '1' then
					next_state	<= GW_SHIFT;
				else
					next_state	<= GW_UPDATE;
				end if;
			
			when GW_UPDATE =>
				next_state	<= MON_LOAD_VALUE;
			
			when MON_LOAD_VALUE =>
				next_state	<= MON_CAPTURE;
			
			when MON_CAPTURE =>
				next_state	<= MON_SHIFT;
			
			when MON_SHIFT =>
				if D = '1' then
					next_state	<= MON_SHIFT;
				elsif F = '1' then
					next_state	<= MON_UPDATE;
				elsif E = '1' then
					next_state	<= LOAD_CONFIG_STORE_RESULT;
				else
					next_state	<= MON_SHIFT; -- this transition has been added in order to cover the entire possibilities otherwise, it would have been impossible to synthesize the design.
				end if;
			
			when MON_UPDATE =>
					next_state	<= GW_PASSIVE;
			
			when LOAD_CONFIG_STORE_RESULT =>
				next_state	<= MON_SHIFT;

			when GW_PASSIVE =>
				next_state	<= IRQ;
			
			when IRQ =>
				--next_state	<= IDLE;
				next_state	<= WAIT_ACK;
		end case;
	end process;
	
	
end;
